package domain.user ;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import persistence.CorruptDataException;
import persistence.PersistenceFactory;
import persistence.repository.UserRepository;
import util.DuplicationException;
import util.Log;
import util.NotFoundException;
import util.Subsystem;

/**
 * the repository for users. any request for a user object goes through here. members are stored to and loaded
 * from persistent media. a URI for that media is needed.
 *
 * the server guarantees that if it empty, then the first user to be created has the id 0.
 *
 * @author idan
 */
public class UserServer
{
	private final UserRepository users ;

	/**
	 * load members from persistent media, no non-member users.
	 * @param persistenceGenerator object to generate persistence controllers.
	 * @throws CorruptDataException if the data is invalid
	 */
	public UserServer(final PersistenceFactory persistenceGenerator) throws CorruptDataException
	{
		Log.getLogger(Subsystem.REPOSITORY).info("starting user server") ;
		this.users = new UserRepository(persistenceGenerator.newMemberPersistence()) ;
		Log.getLogger(Subsystem.REPOSITORY).info("user server started: " + this.toString()) ;
	}

	/**
	 * shut down this server.
	 * @post all information saved to persistence.
	 */
	public void close()
	{
		Log.getLogger(Subsystem.REPOSITORY).info("user server shutting down") ;
		this.users.close() ;
		Log.getLogger(Subsystem.REPOSITORY).info("user server is down") ;
	}

	/**
	 * @param name user name.
	 * @param encryptedPassword encrypted password of this user.
	 * @param privileges privileges of the new user.
	 * @return a new member in the system.
	 * @throws DuplicationException if the member name already exists.
	 * @post return.getName() = name.
	 * @post return.getEncryptedPassword() = encryptedPassword
	 * @post return.getPrivileges() = privileges.
	 * @post member exists in the repository.
	 */
	public Member createMember(final String name, final String encryptedPassword,
		final Set<Privilege> privileges) throws DuplicationException
	{
		Log.getLogger(Subsystem.REPOSITORY).fine("creating new member " + name + ".") ;
		return this.users.create(name, encryptedPassword, privileges) ;
	}

	/**
	 * @param privileges privileges of the new user.
	 * @return a new user in the system.
	 * @post return.getPrivileges() = privileges.
	 * @post user exists on the server, but is not persistent.
	 */
	public User createUser(final Set<Privilege> privileges)
	{
		Log.getLogger(Subsystem.REPOSITORY).fine("creating new user.") ;
		return this.users.createUser(privileges) ;
	}

	/**
	 * @param id id of the user to delete.
	 * @throws NotFoundException if the user was not found.
	 * @pre 'id' is an id of an existing object.
	 * @post the user whose id is 'id' is no longer in the system.
	 * @post id is free to be recycled by another user.
	 */
	public void deleteUser(final long id) throws NotFoundException
	{
		Log.getLogger(Subsystem.REPOSITORY).fine("deleting user " + id) ;
		this.users.destroy(id) ;
	}

	/**
	 * flush everything in memory to persistence.
	 * @post all information is in persistence.
	 */
	public void flush()
	{
		this.users.flush() ;
	}

	/**
	 * flush all information about a member to persistence.
	 * @param member member to flush.
	 * @post the given record is updated in persistence.
	 */
	public void flush(final Member member)
	{
		this.users.flush() ;
	}

	/**
	 * @param ids set of id's to find members for.
	 * @return set of members such that this.contains(member id). the set may be empty.
	 * @throws NotFoundException if one of the id's was not found.
	 * @post result.size() <= ids.size()
	 */
	public Set<Member> getAllMembers(final Collection<Long> ids) throws NotFoundException
	{
		Set<Member> result = new HashSet<Member>() ;
		for (Long id: ids)
		{
			if (this.users.contains(id))
			{
				result.add(this.users.get(id)) ;
			}
		}
		return result ;
	}

	/**
	 * @param ids set of id's to find users for.
	 * @return set of users such that this.contains(user id). the set may be empty.
	 * @throws NotFoundException if one of the id's was not found.
	 * @post result.size() <= ids.size()
	 */
	public Set<User> getAllUsers(final Collection<Long> ids) throws NotFoundException
	{
		Set<User> result = new HashSet<User>() ;
		for (Long id: ids)
		{
			if (this.users.containsUser(id))
			{
				result.add(this.users.getUser(id)) ;
			}
		}
		return result ;
	}

	/**
	 * @param id id of the member to get.
	 * @return member whose id is 'id'.
	 * @throws NotFoundException if the user does not exist.
	 * @pre there exists a user whose id is 'id'.
	 */
	public Member getMember(final long id) throws NotFoundException
	{
		return this.users.get(id) ;
	}

	/**
	 * @param name name of the member to get.
	 * @return a member whose name is 'name'. if the member does not exist, null is returned.
	 */
	public Member getMember(final String name)
	{
		return this.users.getMemberByName(name) ;
	}

	/**
	 * @param id id of the user to get.
	 * @return user whose id is 'id'.
	 * @throws NotFoundException if the user does not exist.
	 * @pre there exists a user whose id is 'id'.
	 */
	public User getUser(final long id) throws NotFoundException
	{
		return this.users.getUser(id) ;
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString()
	{
		return "members from " + this.users.toString() ;
	}

	/**
	 * @param id id to check.
	 * @return 'true' if there exists a user whose id is 'id'.
	 */
	public boolean userExists(final long id)
	{
		return this.users.containsUser(id) ;
	}

}
